﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Animation;
using System.Windows.Media.Media3D;
using System.ServiceModel;
using System.ServiceModel.Discovery;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;


namespace WindSim
{
    [CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)]
    public partial class WindSimModel : BaseClient, INotifyPropertyChanged, SimServiceReference.SimulationServiceCallback
    {
        //Dependency Properties
        public static readonly DependencyProperty RPMValueProperty = DependencyProperty.Register("RPM", typeof(double), typeof(WindSimModel), new FrameworkPropertyMetadata(0.0));
        public static readonly DependencyProperty CurrentPowerValueProperty = DependencyProperty.Register("CurrentPower", typeof(double), typeof(WindSimModel), new FrameworkPropertyMetadata(0.0));

        //Client properties
        private string _clientName = "Model";
        private Guid _guid = Guid.NewGuid();
        private SimServiceReference.ClientType _clientType = SimServiceReference.ClientType.Model;
        private bool _isConnected = false;
        private bool _isSearching = true;
        public override string ClientName
        {
            get { return _clientName; }
            set
            {
                _clientName = value;
                NotifyPropertyChanged("ClientName");
                _client.SendNameToController(_guid, value);
            }
        }
        public override Guid ClientGuid
        {
            get { return _guid; }
        }
        public override SimServiceReference.ClientType ClientType
        {
            get { return _clientType; }
        }
        public override bool IsConnected
        {
            get { return _isConnected; }
            set
            {
                _isConnected = value;
                NotifyPropertyChanged("IsConnected");
                if (value)
                {
                    _client.RegisterClient(_guid, _clientType, new SimServiceReference.ModelData()
                    {
                        Name = _clientName,
                        Location = this.Location,
                        RPM = this.RPM,
                        Energy = CurrentPower,
                        FunctionstateModel = Functionstate,
                        PowerstateModel = Powerstate,
                        DayTime = this.DayTime,
                        ControlType = SimServiceReference.SimControlType.Manual
                    });
                }
            }
        }
        public override bool IsSearching
        {
            get { return _isSearching; }
            set { _isSearching = value; NotifyPropertyChanged("IsSearching"); }
        }

        //Calculation parameter
        private const double _startThreshold = 3.5;
        private const double _stopThreshold = 25;
        private const double _windToRPMFactor = 0.4;
        private const double _windToAnimationFactor = 0.05;
        private const double _rpmMin = 7.5;
        private const double _rpmMin2 = 8.9;
        private const double _rpmMax = 17.5;
        private const double _powerMin = 10000;
        private const double _powerMax = 2050000;
        private const double _rpmToPowerFactor = 237209;

        //Model properties
        #region Own
        private SimServiceReference.PowerState _powerstate = SimServiceReference.PowerState.Off;
        private SimServiceReference.FunctionState _functionstate = SimServiceReference.FunctionState.OK;
        private double _headDirection = 0.0;
        private string _location = "Timbuktu";
        public SimServiceReference.PowerState Powerstate
        {
            get { return _powerstate; }
            set
            {
                _powerstate = value;
                NotifyPropertyChanged("Powerstate");
                _client.SendPowerStateToController(_guid, value); 
                if (value == SimServiceReference.PowerState.On)
                {
                    Functionstate = SimServiceReference.FunctionState.OK;                    
                    InitSystem();
                }
                else if (value == SimServiceReference.PowerState.Off)
                {
                    Functionstate = SimServiceReference.FunctionState.OK;
                    if (sbRotorRotate.GetCurrentState(this) == ClockState.Active ||
                        sbRotorStart.GetCurrentState(this) == ClockState.Active)
                    {
                        StopRotorSlowly(WindSpeed);
                    }
                }
            }
        }
        public SimServiceReference.FunctionState Functionstate
        {
            get { return _functionstate; }
            set 
            {
                _functionstate = value;
                NotifyPropertyChanged("Functionstate"); 
                _client.SendFunctionStateToController(_guid, value);
                if (value == SimServiceReference.FunctionState.OK)
                {
                    InitSystem();
                }
                else if (value == SimServiceReference.FunctionState.Error)
                {
                    if (sbRotorRotate.GetCurrentState(this) == ClockState.Active ||
                        sbRotorStart.GetCurrentState(this) == ClockState.Active)
                    {
                        StopRotorSlowly(WindSpeed);
                    }
                }
            }
        }
        public double HeadDirection
        {
            get { return _headDirection; }
            set
            {
                _headDirection = value;
                NotifyPropertyChanged("HeadDirection");
            }
        }
        public double RPM
        {
            get { return (double)GetValue(RPMValueProperty); }
            set { SetValue(RPMValueProperty, value); }
        }
        public double CurrentPower
        {
            get { return (double)GetValue(CurrentPowerValueProperty); }
            set { SetValue(CurrentPowerValueProperty, value); }
        }        
        public string Location
        {
            get { return _location; }
            set
            {
                _location = value;
                NotifyPropertyChanged("Location");
                _client.SendLocationToController(_guid, value);
            }
        }
        #endregion
        #region From Controller
        private static double _windSpeed = 0.0;
        private double _windDirection;
        private DateTime _dayTime;
        private SimServiceReference.SimControlType _controlType = SimServiceReference.SimControlType.Manual; 
        public double WindSpeed
        {
            get { return _windSpeed; }
            set
            {
                _windSpeed = value;
                NotifyPropertyChanged("WindSpeed");
                if (Powerstate == SimServiceReference.PowerState.On && Functionstate == SimServiceReference.FunctionState.OK)
                {
                    try
                    {
                        if (value < _startThreshold)
                        {
                            if (sbRotorStart.GetCurrentState(this) == ClockState.Active || sbRotorRotate.GetCurrentState(this) == ClockState.Active)
                            {
                                StopRotorFast(value);
                            }
                        }
                        else if (value >= _startThreshold && value <= _stopThreshold)
                        {
                            if (!(sbRotorRotate.GetCurrentState(this) == ClockState.Active))
                            {
                                StartRotor(value);
                            }
                            else if (sbRotorRotate.GetCurrentState(this) == ClockState.Active)
                            {
                                InvokeAction(() => 
                                {
                                    sbRotorRotate.SetSpeedRatio(this, (_windToAnimationFactor * value)); 
                                    SetCurrentValue(RPMValueProperty, _windToRPMFactor * WindSpeed + _rpmMin); 
                                });
                            }
                        }
                        else if (value > _stopThreshold)
                        {
                            if (sbRotorStart.GetCurrentState(this) == ClockState.Active || sbRotorRotate.GetCurrentState(this) == ClockState.Active)
                            {
                                StopRotorSlowly(value);
                            }
                        }
                    }
                    catch { }
                }
            }
        }
        public double WindDirection
        {
            get { return _windDirection; }
            set
            {
                _windDirection = value;
                NotifyPropertyChanged("WindDirection");
                if (Powerstate == SimServiceReference.PowerState.On && Functionstate == SimServiceReference.FunctionState.OK)
                {
                    InvokeAction(() =>
                    {
                        try
                        {
                            Rotation3DAnimation headAnimation = new Rotation3DAnimation();
                            headAnimation.Duration = TimeSpan.FromSeconds(3);
                            headAnimation.To = new AxisAngleRotation3D(new Vector3D(0, 0, 1), WindDirection);
                            HeadRotation.BeginAnimation(RotateTransform3D.RotationProperty, headAnimation);
                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show(ex.Message);
                        }
                    });
                }
            }
        }
        public DateTime DayTime
        {
            get { return _dayTime; }
            set
            {
                _dayTime = value;
                NotifyPropertyChanged("DayTime");
            }
        }
        public SimServiceReference.SimControlType ControlType
        {
            get { return _controlType; }
            set { _controlType = value; NotifyPropertyChanged("ControlType"); }
        }
        #endregion

        //Animation
        Storyboard sbRotorStart;
        Storyboard sbRotorRotate;
        Storyboard sbRotorStop;

        //Constructor
        public WindSimModel()
        {
            InitializeComponent();

            DependencyPropertyDescriptor RPMValuePropertyDescriptor = DependencyPropertyDescriptor.FromProperty(RPMValueProperty, typeof(WindSimModel));
            RPMValuePropertyDescriptor.AddValueChanged(this, OnRPMValuePropertyChanged);

            DependencyPropertyDescriptor CurrentPowerValuePropertyDescriptor = DependencyPropertyDescriptor.FromProperty(CurrentPowerValueProperty, typeof(WindSimModel));
            CurrentPowerValuePropertyDescriptor.AddValueChanged(this, OnRCurrentPowerValuePropertyChanged);

            DayTime = new DateTime(1, 1, 1, 14, 00, 00);

            sbRotorStart = (Storyboard)FindResource("StartRotor");
            sbRotorStart.Begin(this, true);
            sbRotorStart.Stop(this);
            sbRotorStart.Completed += (sender, e) => RotateRotor(WindSpeed);

            sbRotorStop = (Storyboard)FindResource("StopRotorSlow");
            sbRotorStop.Begin(this, true);
            sbRotorStop.Stop(this);

            sbRotorRotate = (Storyboard)FindResource("RotateRotor");
            sbRotorRotate.Begin(this, true);
            sbRotorRotate.Stop(this);
        }

        //Callbacks
        #region Hier nicht implementierte Methoden (nur für Controller benötigt)
        public void NotifyModelRegistered(Guid Id, string name, SimServiceReference.ClientType type)
        {
            throw new NotImplementedException("Wird nur vom Controller ausgerufen!");
        }
        public void NotifyModelUnregistered(Guid Id)
        {
            throw new NotImplementedException("Wird nur vom Controller ausgerufen!");
        }
        public void NotifyControllerModelRegistered(Guid Id, SimServiceReference.ModelData modelData)
        {
            throw new NotImplementedException("Wird nur vom Controller ausgerufen!");
        }
        public void NotifyControllerModelUnregistered(Guid Id)
        {
            throw new NotImplementedException("Wird nur vom Controller ausgerufen!");
        }
        public void NotifyControllerPowerStateChanged(Guid Id, SimServiceReference.PowerState State)
        {
            throw new NotImplementedException("Wird nur vom Controller ausgerufen!");
        }
        public void NotifyControllerRPMChanged(Guid Id, double RPM)
        {
            throw new NotImplementedException("Wird nur vom Controller ausgerufen!");
        }
        public void NotifyControllerEnergyChanged(Guid Id, double Energy)
        {
            throw new NotImplementedException("Wird nur vom Controller ausgerufen!");
        }
        public void NotifyControllerFunctionStateChanged(Guid Id, SimServiceReference.FunctionState State)
        {
            throw new NotImplementedException("Wird nur vom Controller ausgerufen!");
        }
        public void NotifyControllerNameChanged(Guid Id, string Name)
        {
            throw new NotImplementedException("Wird nur vom Controller ausgerufen!");
        }
        public void NotifyControllerLocationChanged(Guid Id, string Location)
        {
            throw new NotImplementedException("Wird nur vom Controller ausgerufen!");
        }
        #endregion
        public void NotifyModelPowerStateChanged(SimServiceReference.PowerState State)
        {
            Powerstate = State;
        }
        public void NotifyModelWindDirectionChanged(double DirectionAngle)
        {
            WindDirection = DirectionAngle;
        }
        public void NotifyModelWindSpeedChanged(double Speed)
        {
            WindSpeed = Speed;
        }
        public void NotifyModelFunctionStateChanged(SimServiceReference.FunctionState State)
        {
            Functionstate = State;
        }
        public void NotifyModelDayTimeChanged(DateTime Time)
        {
            DayTime = Time;
        }
        public void NotifyModelControlTypeChanged(SimServiceReference.SimControlType ControlType)
        {
            this.ControlType = ControlType;
        }

        //Methods
        private void InitSystem()
        {
            WindDirection = WindDirection;
            WindSpeed = WindSpeed;
        }
        private void StartRotor(double WindSpeed)
        {
            InvokeAction(() =>
            {
                sbRotorStop.Stop(this);
                sbRotorRotate.Stop(this);
                sbRotorStart = (Storyboard)FindResource("StartRotor");
                sbRotorStart.Begin(this, true);
                sbRotorStart.SetSpeedRatio(this, (_windToAnimationFactor * WindSpeed));

                DoubleAnimation da = new DoubleAnimation();
                da.SpeedRatio = (_windToAnimationFactor * WindSpeed);
                da.To = (_windToRPMFactor * WindSpeed) + _rpmMin;
                da.Duration = new Duration(TimeSpan.FromSeconds(2));
                ExponentialEase ee = new ExponentialEase();
                ee.Exponent = 2;
                ee.EasingMode = EasingMode.EaseIn;
                da.EasingFunction = ee;
                BeginAnimation(RPMValueProperty, da);

                DoubleAnimation da2 = new DoubleAnimation();
                da2.SpeedRatio = (_windToAnimationFactor * WindSpeed);
                da2.To = _rpmToPowerFactor * ((_windToRPMFactor * WindSpeed + _rpmMin) - _rpmMin2) + _powerMin;
                da2.Duration = new Duration(TimeSpan.FromSeconds(2));
                ExponentialEase ee2 = new ExponentialEase();
                ee2.Exponent = 2;
                ee2.EasingMode = EasingMode.EaseIn;
                da2.EasingFunction = ee2;
                BeginAnimation(CurrentPowerValueProperty, da2);
            });
        }
        private void RotateRotor(double WindSpeed)
        {
            InvokeAction(() =>
            {
                sbRotorRotate = (Storyboard)FindResource("RotateRotor");
                sbRotorRotate.Begin(this, true);
                sbRotorRotate.SetSpeedRatio(this, (_windToAnimationFactor * WindSpeed));
            });
        }
        private void StopRotorSlowly(double WindSpeed)
        {
            InvokeAction(() =>
            {
                sbRotorStart.Stop(this);
                sbRotorRotate.Stop(this);
                sbRotorStop = (Storyboard)FindResource("StopRotorSlow");
                sbRotorStop.Begin(this, true);
                sbRotorStop.SetSpeedRatio(this, (_windToAnimationFactor * WindSpeed));

                if (WindSpeed > 0.0)
                {
                    DoubleAnimation da = new DoubleAnimation();
                    da.SpeedRatio = (_windToAnimationFactor * WindSpeed);
                    da.To = 0.0;
                    da.Duration = new Duration(TimeSpan.FromSeconds(2));
                    ExponentialEase ee = new ExponentialEase();
                    ee.Exponent = 2;
                    ee.EasingMode = EasingMode.EaseOut;
                    da.EasingFunction = ee;
                    BeginAnimation(RPMValueProperty, da);

                    DoubleAnimation da2 = new DoubleAnimation();
                    da2.SpeedRatio = (_windToAnimationFactor * WindSpeed);
                    da2.To = 0.0;
                    da2.Duration = new Duration(TimeSpan.FromSeconds(2));
                    ExponentialEase ee2 = new ExponentialEase();
                    ee2.Exponent = 2;
                    ee2.EasingMode = EasingMode.EaseOut;
                    da2.EasingFunction = ee2;
                    BeginAnimation(CurrentPowerValueProperty, da2);
                }
            });
        }
        private void StopRotorFast(double WindSpeed)
        {
            InvokeAction(() =>
            {
                sbRotorStart.Stop(this);
                sbRotorRotate.Stop(this);
                sbRotorStop = (Storyboard)FindResource("StopRotorFast");
                sbRotorStop.Begin(this, true);
                sbRotorStop.SetSpeedRatio(this, (_windToAnimationFactor * WindSpeed));

                if (WindSpeed > 0.0)
                {
                    DoubleAnimation da = new DoubleAnimation();
                    da.SpeedRatio = (_windToAnimationFactor * WindSpeed);
                    da.To = 0.0;
                    da.Duration = new Duration(TimeSpan.FromSeconds(0.5));
                    ExponentialEase ee = new ExponentialEase();
                    ee.Exponent = 2;
                    ee.EasingMode = EasingMode.EaseOut;
                    da.EasingFunction = ee;
                    BeginAnimation(RPMValueProperty, da);

                    DoubleAnimation da2 = new DoubleAnimation();
                    da2.SpeedRatio = (_windToAnimationFactor * WindSpeed);
                    da2.To = 0.0;
                    da2.Duration = new Duration(TimeSpan.FromSeconds(0.5));
                    ExponentialEase ee2 = new ExponentialEase();
                    ee2.Exponent = 2;
                    ee2.EasingMode = EasingMode.EaseOut;
                    da2.EasingFunction = ee2;
                    BeginAnimation(CurrentPowerValueProperty, da2);
                }
            });
        }
        private void CalculateCurrentPower(double rpm)
        {
            if (rpm >= _rpmMin2)
            {
                SetCurrentValue(CurrentPowerValueProperty, (_rpmToPowerFactor * (rpm - _rpmMin2)) + _powerMin);
            }
        }

        //Eventhandler
        private void Window_Loaded_1(object sender, RoutedEventArgs e)
        {            
            DiscoverService();
        }
        private void Window_Closing_1(object sender, CancelEventArgs e)
        {
            try
            {
                _client.UnRegisterClient(_guid, _clientType);
            }
            catch { }
        }
        private void tbName_TextChanged(object sender, TextChangedEventArgs e)
        {
            try
            {
                TextBox tb = sender as TextBox;
                ClientName = tb.Text;
            }
            catch { }
        }
        private void tbLocation_TextChanged(object sender, TextChangedEventArgs e)
        {
            try
            {
                TextBox tb = sender as TextBox;
                Location = tb.Text;
            }
            catch { }
        }
        private void OnRPMValuePropertyChanged(object sender, EventArgs e)
        {
            _client.SendRPMToController(_guid, RPM);
            CalculateCurrentPower(RPM);  
        }
        private void OnRCurrentPowerValuePropertyChanged(object sender, EventArgs e)
        {
            _client.SendEnergyToController(_guid, CurrentPower);
        }
        private void btnRetrySearchService_Click(object sender, RoutedEventArgs e)
        {
            DiscoverService();
        }
    }
}